package com.geekhole.demo.user.application.v4;

import com.geekhole.demo.user.domain.Agg;
import com.geekhole.demo.user.domain.DomainServiceContext;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.repository.CrudRepository;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;

public abstract class BaseV4Application {
    private static final Logger LOGGER = LoggerFactory.getLogger(BaseV4Application.class);

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 创建 Creator，已完成对创建流程的组装
     * @param repository
     * @param <A>
     * @param <CONTEXT>
     * @return
     */
    protected <A extends Agg, CONTEXT extends DomainServiceContext> Creator<A, CONTEXT> creatorFor(CrudRepository<A, ?> repository){
        return new Creator<A, CONTEXT>(repository);
    }

    /**
     * 创建 Updater，已完成对更新流程的组装
     * @param aggregateRepository
     * @param <A>
     * @param <CONTEXT>
     * @return
     */
    protected <A extends Agg, CONTEXT extends DomainServiceContext> Updater<A, CONTEXT> updaterFor(CrudRepository<A, ?> aggregateRepository){
        return new Updater<A, CONTEXT>(aggregateRepository);
    }

    /**
     * 创建流程的 Builder，主要：
     * 1. 组装流程
     * 2. 执行流程
     * @param <A>
     * @param <CONTEXT>
     */
    protected class Creator<A extends Agg, CONTEXT extends DomainServiceContext>{
        // 标准仓库
        private final CrudRepository<A, ?> aggregateRepository;

        // 实例化器，用于完成对 聚合 的实例化
        private Function<CONTEXT, A> instanceFun;

        // 默认的操作成功处理器，在操作成功后进行回调
        private BiConsumer<A, CONTEXT> successFun = (agg, context)->{
            LOGGER.info("success to handle {} and sync {} to DB", context, agg);
        };

        // 默认的异常处理器，在操作失败抛出异常时进行回调
        private BiConsumer<Exception, CONTEXT> errorFun = (exception, context) -> {
            LOGGER.error("failed to handle {}", context, exception);
            if (exception instanceof RuntimeException){
                throw (RuntimeException) exception;
            }
            throw new RuntimeException(exception);
        };

        // 聚合业务操作方法回调，最主要的扩展点，用于执行 聚合上的业务方法
        private BiConsumer<A, CONTEXT> updateFun = (a, context) -> {};


        Creator(CrudRepository<A, ?> aggregateRepository) {
            Preconditions.checkArgument(aggregateRepository != null);
            this.aggregateRepository = aggregateRepository;
        }

        /**
         * 设置 聚合的实例化器
         * @param instanceFun
         * @return
         */
        public Creator<A, CONTEXT> instance(Function<CONTEXT, A> instanceFun){
            Preconditions.checkArgument(instanceFun != null);
            this.instanceFun = instanceFun;
            return this;
        }

        /**
         * 增加 聚合上的业务操作，链式模式，可以绑定多的 业务操作
         * @param updater
         * @return
         */
        public Creator<A, CONTEXT> update(BiConsumer<A, CONTEXT> updater){
            Preconditions.checkArgument(updater != null);
            this.updateFun = this.updateFun.andThen(updater);
            return this;
        }

        /**
         * 增加 成功处理器，链式模式，可以绑定多个处理器
         * @param onSuccessFun
         * @return
         */
        public Creator<A, CONTEXT> onSuccess(BiConsumer<A, CONTEXT>  onSuccessFun){
            Preconditions.checkArgument(onSuccessFun != null);
            this.successFun = onSuccessFun.andThen(this.successFun);
            return this;
        }

        /**
         * 增加 异常处理器，链式模式，可以绑定多个处理器
         * @param errorFun
         * @return
         */
        public Creator<A, CONTEXT> onError(BiConsumer<Exception, CONTEXT>  errorFun){
            Preconditions.checkArgument(errorFun != null);
            this.errorFun = errorFun.andThen(this.errorFun);
            return this;
        }

        /**
         * 执行 创建流程
         * @param context
         * @return
         */
        public A call(CONTEXT context){
            Preconditions.checkArgument(this.instanceFun != null, "instance fun can not be null");
            Preconditions.checkArgument(this.aggregateRepository != null, "aggregateRepository can not be null");

            A a = null;
            try{
                // 实例化 聚合根
                a = this.instanceFun.apply(context);

                // 在聚合根上执行业务操作
                this.updateFun.accept(a, context);

                // 持久化聚合根到 DB
                this.aggregateRepository.save(a);

                // 发布领域事件，并进行清理
                if (eventPublisher != null){
                    // 1. 发布领域事件
                    a.foreachEvent(eventPublisher::publishEvent);

                    // 2. 清理领域事件
                    a.clearEvents();
                }

                // 调用 成功回调器
                this.successFun.accept(a, context);
            }catch (Exception e){
                // 调用 异常回调器
                this.errorFun.accept(e, context);
            }
            return a;
        }
    }

    /**
     * 更新流程的 Builder，主要：
     * 1. 组装流程
     * 2. 执行流程
     * @param <A>
     * @param <CONTEXT>
     */
    protected class Updater<A extends Agg, CONTEXT extends DomainServiceContext> {
        // 标准仓库
        private final CrudRepository<A, ?> aggregateRepository;
        // 聚合加载器，用于从 DB 中加载 聚合对象
        private Function<CONTEXT, A> loadFun;
        // 聚合丢失处理器，聚合丢失时进行回调
        private Consumer<CONTEXT> onNotExistFun = context -> {};
        // 成功回调器，链式模式，在操作成功时调用
        private BiConsumer<A, CONTEXT> successFun = (agg, context)->{
            LOGGER.info("success to handle {} and sync {} to DB", context, agg);
        };
        // 异常回调器，链式模式，在操作失败抛出异常时调用
        private BiConsumer<Exception, CONTEXT> errorFun = (exception, context) -> {
            LOGGER.error("failed to handle {}", context, exception);
            if (exception instanceof RuntimeException){
                throw (RuntimeException) exception;
            }
            throw new RuntimeException(exception);
        };
        // 业务更新器，对聚合进行业务操作
        private BiConsumer<A, CONTEXT> updateFun = (a, context) -> {};


        Updater(CrudRepository<A, ?> aggregateRepository) {
            this.aggregateRepository = aggregateRepository;
        }

        /**
         * 设置 聚合对象加载器，用于从 DB 中加载 聚合
         * @param loader
         * @return
         */
        public Updater<A, CONTEXT> loader(Function<CONTEXT, A> loader){
            Preconditions.checkArgument(loader != null);
            this.loadFun = loader;
            return this;
        }

        /**
         * 增加 业务执行器，链式模式，可以绑定多个执行器
         * @param updateFun
         * @return
         */
        public Updater<A, CONTEXT> update(BiConsumer<A, CONTEXT> updateFun){
            Preconditions.checkArgument(updateFun != null);
            this.updateFun = updateFun.andThen(this.updateFun);
            return this;
        }

        /**
         * 增加 成功回调器，链式模式，可以绑定多个回调器
         * @param onSuccessFun
         * @return
         */
        public Updater<A, CONTEXT> onSuccess(BiConsumer<A, CONTEXT>  onSuccessFun){
            Preconditions.checkArgument(onSuccessFun != null);
            this.successFun = onSuccessFun.andThen(this.successFun);
            return this;
        }

        /**
         * 增加 异常回调器，链式模式，可以绑定多个回调器
         * @param errorFun
         * @return
         */
        public Updater<A, CONTEXT> onError(BiConsumer<Exception, CONTEXT>  errorFun){
            Preconditions.checkArgument(errorFun != null);
            this.errorFun = errorFun.andThen(this.errorFun);
            return this;
        }

        /**
         * 增加 聚合丢失处理器，链式模式，可以绑定多个回调器
         * @param onNotExistFun
         * @return
         */
        public Updater<A, CONTEXT> onNotFound(Consumer<CONTEXT>  onNotExistFun){
            Preconditions.checkArgument(onNotExistFun != null);
            this.onNotExistFun = onNotExistFun.andThen(this.onNotExistFun);
            return this;
        }

        /**
         * 执行更新流程
         * @param context
         * @return
         */
        public A call(CONTEXT context){
            Preconditions.checkArgument(this.aggregateRepository != null, "aggregateRepository can not be null");
            Preconditions.checkArgument(this.loadFun != null, "loader can not both be null");
            A a = null;
            try {
                // 从 DB 中加载 聚合根
                a = this.loadFun.apply(context);

                if (a == null){
                    // 聚合根不存在，回调 聚合丢失处理器
                    this.onNotExistFun.accept(context);
                }

                // 在聚合之上，执行业务操作
                updateFun.accept(a, context);

                // 对聚合进行持久化处理
                this.aggregateRepository.save(a);

                // 发布并清理事件
                if (eventPublisher != null){
                    // 1. 发布领域事件
                    a.foreachEvent(eventPublisher::publishEvent);

                    // 2. 清理领域事件
                    a.clearEvents();
                }
                // 操作成功回调
                this.successFun.accept(a, context);

            }catch (Exception e){
                // 异常回调
                this.errorFun.accept(e, context);
            }
            return a;
        }

    }
}
